'use strict'

// <--    还原前    -->
var _2$SS = function(_SSz, _1111) {
    var _l1L1 = [46222, '\x74\x61\x43\x61\x70\x74\x63\x68\x61\x42\x6c\x6f\x62', '\x74', '\x61', '\x73', '\x6c', '\x64', '\x69', .3834417654519915, '\x65\x6e\x63\x72\x79\x70\x74\x4a', '\x73\x6f', '\x6e', 49344];
 
    var _2Szs = _l1L1[5] + _l1L1[7] + (_l1L1[4] + _l1L1[2])
      , _I1il1 = _l1L1[9] + (_l1L1[10] + _l1L1[11]);
 
    var _0ooQoO = _l1L1[0];
 
    var _$Z22 = _l1L1[12]
      , _2sS2 = _l1L1[8];
 
    return _l1L1[6] + _l1L1[3] + _l1L1[1];
};

/**
 * <--    特点    -->
 * 1. 将数据集中存储在一个数组中，后续使用时直接从数组中取
 * 2. 存在16进制字符串
 * 3. 可能还需要进行字面量折叠
 * 4. 还可以移除僵尸变量
 * 
 * <--    解决办法    -->
 * 1. 从剪树枝的角度说，首先应该移除僵尸变量
 * 2. 数组成员消解
 * 3. 16进制字符串还原
 * 4. 字面量折叠
 * 
 * <--    冗余判断    -->
 * 该数组没有被重新赋值过
 * 
 **/
// <--    还原后    -->
var _2$SS = function (_SSz, _1111) {
    return "dataCaptchaBlob";
};

const {parse} = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const t = require('@babel/types');
const generator = require('@babel/generator').default;

let xcode = `var _2$SS = function(_SSz, _1111) {
    var _l1L1 = [46222, '\x74\x61\x43\x61\x70\x74\x63\x68\x61\x42\x6c\x6f\x62', '\x74', '\x61', '\x73', '\x6c', '\x64', '\x69', .3834417654519915, '\x65\x6e\x63\x72\x79\x70\x74\x4a', '\x73\x6f', '\x6e', 49344];
 
    var _2Szs = _l1L1[5] + _l1L1[7] + (_l1L1[4] + _l1L1[2])
      , _I1il1 = _l1L1[9] + (_l1L1[10] + _l1L1[11]);
 
    var _0ooQoO = _l1L1[0];
 
    var _$Z22 = _l1L1[12]
      , _2sS2 = _l1L1[8];
 
    return _l1L1[6] + _l1L1[3] + _l1L1[1];
};`

const visitor = {
    FunctionExpression:function(path){
        if(path.parentPath.get('id').node.name != '_2$SS'){return}

        // path.scope.dump();
        path.traverse({
            VariableDeclarator:function(path){
                                let binding = path.scope.getBinding(path.get('id').node.name);
                                if(binding && binding.constant && !binding.referenced){
                                    path.remove();
                                }},});
        
        // path.scope.dump();
        // # FunctionExpression
        // - _SSz { constant: true, references: 0, violations: 0, kind: 'param' }
        // - _1111 { constant: true, references: 0, violations: 0, kind: 'param' }
        // - _l1L1 { constant: true, references: 13, violations: 0, kind: 'var' }
        // # Program
        // - _2$SS { constant: true, references: 0, violations: 0, kind: 'var' }
        path.scope.crawl();    // 只是动态更新到path，scope属性层级，对于scope.bindings更新了，但是binding.referencesPaths,constant等属性还没有更新
        // path.scope.dump();     // 对比crawl()前后两次dump()，scope.bindings是一致的，_l1L1.references就有变化，明显是通过crawl()将作用域所属所有的绑定的状态更新了
        // # FunctionExpression
        // - _SSz { constant: true, references: 0, violations: 0, kind: 'param' }
        // - _1111 { constant: true, references: 0, violations: 0, kind: 'param' }
        // - _l1L1 { constant: true, references: 3, violations: 0, kind: 'var' }
        // # Program
        // - _2$SS { constant: true, references: 0, violations: 0, kind: 'var' }
        path.traverse({
            VariableDeclarator:function(path){
                                if(!t.isArrayExpression(path.get('init').node)){return}

                                let binding = path.scope.getBinding(path.get('id').node.name);

                                if(!binding || !binding.constant){return}

                                for(let refPath of binding.referencePaths){
                                    let memberExp = refPath.parentPath;
                                    if(!t.isMemberExpression(memberExp) || !t.isNumericLiteral(memberExp.get('property').node)){continue}
                                    let i = memberExp.get('property').node.value;
                                    memberExp.replaceInline(path.get('init.elements.'+i).node);
                                }
                                path.scope.crawl();    // 更新作用域状态
                                // path.scope.dump();
                                
                                if(!path.scope.getBinding(path.get('id').node.name).referenced){
                                    path.remove();
                                }},});
        path.scope.crawl();

        path.traverse({
            StringLiteral:function(path){
                path.node.extra = {};
            },
        });

        path.traverse({
            "BinaryExpression|CallExpression|ConditionalExpression|MemberExpression|SequenceExpression|ParenthesizedExpression|UnaryExpression":function(path){
                let {confident, value} = path.evaluate();
                if(confident){
                    path.replaceInline(t.valueToNode(value));
                }
            },
        });

        path.scope.crawl();
    },
}

let ast = parse(xcode);
traverse(ast, visitor);
let {code} = generator(ast);
console.log(code)



